package com.hexun.dubbo.filter;

import com.hexun.dubbo.utils.JacksonUtils;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;

import javax.security.auth.callback.Callback;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

public class JsonpResponseWrapper extends HttpServletResponseWrapper {

    protected ByteArrayServletOutputStream buffer;
    protected PrintWriter bufferWriter;
    protected boolean committed = false;
    /**
     * json mime type
     */
    protected static final String[] jsonMimeTypes = new String[]{"application/json", "application/x-json", "text/json", "text/x-json"};
    /**
     * response 的content type 类型是否是json
     */
    protected boolean sourceIsJson = false;

    /**
     * callback 对应的key
     */
    private String callback = "callback";

    /**
     * 构造器
     *
     * @param response source response
     */
    public JsonpResponseWrapper(HttpServletResponse response, String callback) {
        super(response);
        buffer = new ByteArrayServletOutputStream();
        this.callback = callback;
    }

    /**
     * 判断是否是json
     *
     * @param contentType
     * @return
     */
    protected boolean isJson(String contentType) {
        if (contentType == null || contentType.trim().equals("")) {
            return false;
        }
        for (String jsonMimeType : jsonMimeTypes) {
            if (contentType.contains(jsonMimeType)) {
                return true;
            }
        }
        return false;
    }


    public void flushWrapper() throws IOException {
        if (bufferWriter != null)
            bufferWriter.close();
        if (buffer != null)
            buffer.close();
        byte[] content = wrap(buffer.toByteArray());
        getResponse().setContentLength(content.length);
        getResponse().getOutputStream().write(content);
        getResponse().flushBuffer();
        committed = true;
    }

    /**
     * 覆盖父类方法,不对Content-Type进行设置
     *
     * @param name
     * @param value
     */
    @Override
    public void setHeader(String name, String value) {
        if (name.equals("Content-Type")) {
            //判断源response content type 是否为json
            sourceIsJson = isJson(value);
            if (sourceIsJson) {
                super.setHeader(name, "text/javascript;charset=utf-8");
                return;
            }
        }
        super.setHeader(name, value);
    }

    /**
     * 覆盖父类方法,不对Content-Type进行设置
     *
     * @param name
     * @param value
     */
    @Override
    public void addHeader(String name, String value) {
        //System.out.print("head name=" + name + ";value=" + value);
        if (name.equals("Content-Type")) {
            //判断源response content type 是否为json
            sourceIsJson = isJson(value);
            if (sourceIsJson) {
                super.addHeader(name, "text/javascript;charset=utf-8");
                return;
            }
        }
        super.addHeader(name, value);
    }

    @Override
    public void setContentType(String type) {

    }


    /**
     * 定义的可以由子类覆盖的方法，用于改写输出内容
     *
     * @param content
     * @return
     * @throws IOException
     */
    public byte[] wrap(byte[] content) throws IOException {
        if (!sourceIsJson) {
            return content;
        }
        String contentStr = new String(content, getCharacterEncoding());
        boolean beJSON = JacksonUtils.mayBeJSON(contentStr);
        if (!beJSON) {
            //不可能为json，则需要quote
            contentStr = JacksonUtils.quote(contentStr);
        }
        return (callback + "(" + contentStr + ");").getBytes(getCharacterEncoding());
    }

    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        return buffer;
    }

    /**
     * The default behavior of this method is to return getWriter() on the
     * wrapped response object.
     */

    @Override
    public PrintWriter getWriter() throws IOException {
        if (bufferWriter == null) {
            bufferWriter = new PrintWriter(
                    new OutputStreamWriter(buffer, this.getCharacterEncoding())
            );
        }
        return bufferWriter;
    }

    @Override
    public void setBufferSize(int size) {
        buffer.enlarge(size);
    }

    @Override
    public int getBufferSize() {
        return buffer.size();
    }

    @Override
    public void flushBuffer() throws IOException {
    }

    @Override
    public boolean isCommitted() {
        return committed;
    }

    @Override
    public void reset() {
        getResponse().reset();
        buffer.reset();
    }

    @Override
    public void resetBuffer() {
        getResponse().resetBuffer();
        buffer.reset();
    }

}
